package com.pansoft.xbrl.xbrljson.convert;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.pansoft.xbrl.xbrljson.config.model.JsonConfigItem;
import com.pansoft.xbrl.xbrljson.config.model.XbrlJsonConfig;
import com.pansoft.xbrl.xbrljson.constant.XbrlConfigConstant;
import com.pansoft.xbrl.xbrljson.model.*;
import com.pansoft.xbrl.xbrljson.util.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

/**
 * 根据xbrl模板和json数据创建xbrl对象
 * @author coolmayi
 * @create 2021/9/5
 */
public class XbrlObjectCreator {

    private static volatile XbrlObjectCreator xbrlCreator ;

    /**
     * 配置项合集
     */
    private HashMap<String, XbrlJsonConfig> xbrlJsonConfigMap;

    public XbrlObjectCreator() {

    }

    /**
     * 获取xbrl数据生成器
     * @return
     */
    public static XbrlObjectCreator getInstance() {
        if (xbrlCreator == null ) {
            synchronized (XbrlObjectCreator.class) {
                if (xbrlCreator == null) {
                    xbrlCreator = new XbrlObjectCreator();
                }
            }
        }
        return xbrlCreator;
    }
    /**
     * 创建xbrl对象
     * @param configObject
     * @param jsonObject
     * @return
     */
    public Xbrl createXbrlData(XbrlJsonConfig configObject, JSONObject jsonObject) {

        if (configObject == null) {
            return null;
        }

        Xbrl xbrlTemp = configObject.getXbrlTemp();

        if (xbrlTemp == null) {
            return null;
        }

        Xbrl xbrlData = (Xbrl)xbrlTemp.clone();


        JsonConfigItem jsonConfigItem = configObject.getJsonConfig();

        if (jsonConfigItem == null) {
            return null;
        }

        // 计算模板中的表达式
        this.calcExp(xbrlData, jsonObject);

        // 转换数据
        HashMap<String, JsonConfigItem> configItemMap = configObject.getJsonConfigMap();
        this.buildJsonDataToInst(xbrlData, jsonObject, configItemMap, "", null);

        return xbrlData;
    }

    /**
     * 生成实例文档数据
     * @param xbrlData
     * @param jsonObject
     * @param configItemMap
     * @param parentId
     * @param parentInstData
     */
    private void buildJsonDataToInst(Xbrl xbrlData, JSONObject jsonObject, HashMap<String, JsonConfigItem> configItemMap,
                                      String parentId, InstData parentInstData) {
        if (jsonObject == null) {
            return ;
        }

        Set<String> keySet = jsonObject.keySet();

        if (keySet == null || keySet.size() == 0) {
            return ;
        }

        String configKey = parentId;
        if (!StringUtil.isBlank(parentId)) {
            configKey = parentId + "-";
        }
        String nodeKey = null;
        for (String key : keySet) {
            nodeKey = configKey + key;
            Object item = jsonObject.get(key);
            JsonConfigItem configItem = configItemMap.get(nodeKey);
            if (configItem == null) {
                // 没有找到字段配置信息，无法生成实例文档数据
                continue;
            }
            InstData instData = this.createInstData(xbrlData, item, configItem, parentInstData);

            if (item instanceof JSONObject) {
                // 存在下级节点
                this.buildJsonDataToInst(xbrlData, (JSONObject)item, configItemMap, nodeKey, instData);
            } else if (item instanceof  List) {
                List<Object> itemList = (List<Object>)item;

                // 第一个节点使用原来的，其他节点克隆父节点
                int childIndex = 0;
                for (Object childItem : itemList) {
                    if (childItem instanceof JSONObject) {
                        // 存在下级节点
                        if (childIndex != 0) {
                            InstData cloneInstData = (InstData)instData.clone();
                            if (parentInstData == null || !parentInstData.addChild(cloneInstData)) {
                                List<InstData> dataList = xbrlData.getInstDataList();
                                dataList.add(cloneInstData);
                            }
                            this.buildJsonDataToInst(xbrlData, (JSONObject)childItem, configItemMap, nodeKey, cloneInstData);
                        } else {
                            this.buildJsonDataToInst(xbrlData, (JSONObject)childItem, configItemMap, nodeKey, instData);
                        }

                    } else {
                        System.out.println("无法解析：" + childItem.toString() + "---" + childItem.getClass().toString());
                    }

                    childIndex++;
                }
            }
        }
    }

    /**
     * 创建实例文档对象
     * @param item
     * @param configItem
     * @return
     */
    private InstData createInstData(Xbrl xbrlData, Object item, JsonConfigItem configItem, InstData parentInstData) {

        InstData instData = new InstData();

        String eleName = configItem.getElementName();
        String nsName = configItem.getElementNsName();
        String dec = configItem.getDecimals();
        String controlData = configItem.getControlData();
        String valueType = configItem.getElementValueType();
        String contextType = configItem.getContextType();
        String unitId = configItem.getUnitId();

        if (StringUtil.isBlank(eleName) || StringUtil.isBlank(nsName)) {
            return null;
        }

        // 判断是否为元组父节点
        if (valueType.equals(XbrlConfigConstant.elementValueType_Tuple)) {

            instData = new InstData();
            HashMap<String, String> nameSpaceMap = xbrlData.getNameSpaceMap();
            instData.setElementName(eleName);
            instData.setElementNameSpaceUri(nameSpaceMap.get(nsName));
            instData.setElementNsName(nsName);
            // 设置下级节点不为空
            instData.setChildList(new ArrayList<InstData>());
            if (parentInstData == null || !parentInstData.addChild(instData)) {
                List<InstData> dataList = xbrlData.getInstDataList();
                dataList.add(instData);
            }
            return instData;
        }

        //TODO 生成维度信息
        List<ContextDim> dimensionList = new ArrayList<ContextDim>();
//        List<ContextDim> sysDimensionList = objectParam.getContextDimList();
//        if (sysDimensionList != null) {
//            dimensionList.addAll(sysDimensionList);
//        }
//        // 判断是否清除了维度
//        if (field.isAnnotationPresent(CleanDimension.class)) {
//            dimensionList.clear();
//        }
//
//        // 判断成员是否有维度注入
//        ObjectConvertParam filedParam = processFiledMember(xbrlData, classObject, field, objectParam);
//        if (filedParam != null) {
//            List<ContextDim> filedDimensionList = filedParam.getContextDimList();
//            if (filedDimensionList != null) {
//                dimensionList.addAll(filedDimensionList);
//            }
//        }




        // 判断数据是否有下级
        String strValue = item.toString();
        Double numValue = null;

        if (!StringUtil.isBlank(dec) && valueType.equals(XbrlConfigConstant.elementValueType_Number)) {
            // 存在小数位数，暂时判定为数值类型
            try {
                numValue = Double.valueOf(strValue);
            } catch (Exception e) {
            }
        } else {
            // 非数值类型
            dec = null;
            unitId = null;
        }
        if (valueType.equals(XbrlConfigConstant.elementValueType_Date)) {
            strValue = DateUtil.formatDateValue(strValue);
        }

        // 生成上下文
        Context context = createContext(xbrlData, dimensionList, contextType);

        // 数据转换器
        if (!StringUtil.isBlank(controlData) && !StringUtil.isBlank(strValue)) {

            String value = PropUtil.getPropValue(controlData, strValue);
            if (!StringUtil.isBlank(value)) {
                strValue = value;
            }
        }

        // 生成实例文档数据
        instData = createInstData(xbrlData, eleName, nsName, dec, unitId, context, strValue, numValue);

        if(valueType.equals(XbrlConfigConstant.elementValueType_Date) && StringUtil.isBlank(instData.getStrValue())){
            instData.setNil("true");
        }

        if (parentInstData == null || !parentInstData.addChild(instData)) {
            List<InstData> dataList = xbrlData.getInstDataList();
            dataList.add(instData);
        }

        return instData;
    }


    /**
     * 生成实例文档数据
     * @param xbrlData
     * @param eleName
     * @param nsName
     * @param dec
     * @param unitId
     * @param context
     * @return
     */
    private InstData createInstData(Xbrl xbrlData, String eleName, String nsName, String dec , String unitId, Context context, String strValue, Double numValue) {

        if (context == null) {
            return null;
        }

        // TODO 判断是否存在重复元素
        InstData instData = new InstData();

        HashMap<String, String> nameSpaceMap = xbrlData.getNameSpaceMap();
        instData.setElementName(eleName);
        instData.setElementNameSpaceUri(nameSpaceMap.get(nsName));
        instData.setElementNsName(nsName);
        instData.setContextRef(context.getId());
        instData.setDecimals(dec);
        instData.setUnitRef(unitId);
        instData.setStrValue(strValue);
        instData.setValueType(InstData.VALUE_TYPE_STRING);
        if (numValue != null) {
            instData.setNumValue(numValue);
            instData.setValueType(InstData.VALUE_TYPE_NUMBER);
        }

//        List<InstData> dataList = xbrlData.getInstDataList();
//        dataList.add(instData);

        return instData;
    }


    /**
     * 生成上下文对象
     * @param xbrlData
     * @param dimensionList
     * @param contextType
     * @return
     */
    private Context createContext(Xbrl xbrlData, List<ContextDim> dimensionList, String contextType) {

        HashMap<String, Context> contextMap = xbrlData.getContextMap();

        String instant = xbrlData.getInstant();
        String startDate = xbrlData.getStartDate();
        String endDate = xbrlData.getEndDate();

        Context context = new Context();
        if (contextType.equals(XbrlConfigConstant.contextType_Instant)) {
            context.setPeriodType(Context.ContextPeriodInstant);
            context.setInstDate(instant);
        } else if (contextType.equals(XbrlConfigConstant.contextType_Duration)) {
            context.setPeriodType(Context.ContextPeriodDuration);
            context.setStartDate(startDate);
            context.setEndDate(endDate);
        }
        if (dimensionList != null) {
            context.setScenarioList(dimensionList);
        }

        String id = "c1";//ContextUtil.createContextId(context);

        if (contextMap.containsKey(id)) {
            return contextMap.get(id);
        } else {
            context.setId(id);
            contextMap.put(id, context);
        }

        return context;
    }

    /**
     * 计算xbrl初始化数据内的公式新
     * @param ret
     * @param jsonObject
     */
    private void calcExp(Xbrl ret, JSONObject jsonObject) {

        if (ret == null || jsonObject == null) {
            return ;
        }

        Entity entity = ret.getDefaultEntity();
        if (entity != null) {
            entity.setIdentifier(this.convertExpValue(entity.getIdentifier(), jsonObject));
        }

        if (!StringUtil.isBlank(ret.getInstant())) {
            ret.setInstant(this.convertExpValue(ret.getInstant(), jsonObject));
        }
    }

    /**
     * 转换含有公式定义的数据项
     * @param expValue
     * @param jsonObject
     * @return
     */
    public String convertExpValue(String expValue, JSONObject jsonObject) {

        String retValue = expValue;
        List<String> paramList =  PatternUtil.getFiledExpList(expValue);
        if (paramList != null && paramList.size() > 0) {
            for (String param : paramList) {
                Object value = this.getExpValue(param.substring(2, param.length() - 1), jsonObject);
                if (value == null) {
                    value = "null";
                }
                retValue = retValue.replace(param, value.toString());
            }
        }

        return retValue;
    }

    /**
     * 根据表达式获取值
     * @param exp
     * @param jsonObject
     * @return
     */
    public Object getExpValue(String exp, JSONObject jsonObject) {

        if (StringUtil.isBlank(exp)) {
            return null;
        }

        if (jsonObject == null) {
            return null;
        }
        String[] paramList = exp.split("\\.");

        // 操作对象
        JSONObject operItem = jsonObject;
        Object retObj = null;
        for (String param : paramList) {
            retObj = null;
            Object item = operItem.get(param);
            if (item == null) {
                return null;
            }
            if (item instanceof JSONObject) {
                operItem = (JSONObject)item;
            } else if (item instanceof JSONArray) {
                operItem = (JSONObject) ((JSONArray) item).get(0);
            } else {
                retObj = item;
            }
        }

        return retObj;
    }

}
